<?php
namespace classlib\database;
use classlib\orm;
use PDO;
class pdomysql extends parser {
    /**
     * Statement的mark列表
     * @var array
     */
    private $marks = array();
    
    /**
     * PDO的statement对象
     * @var PDOStatement
     */
    private $statement;
    
    /**
     * 构造函数
     * @param array $params
     * @param array $options
     */
    public function __construct($params, $options) {
        parent::__construct($params, $options);
    }
    
    /**
     * 连接数据库,如果连接失败,抛出 PDOException 异常
     * (non-PHPdoc)
     * @see parser::connect()
     * @throws
     */
    public function connect() {
        $this->db = new PDO($this->params['dsn'], $this->params['user'], $this->params['password'], $this->options);
        return true;
    }

    /**
     * 解析生成SQL语句
     * {@inheritDoc}
     * @see \classlib\database\parser::parse_sql()
     */
    public function parse_sql($args, $isprint=false) {
        $this->marks = array();
        if (!isset($args['table']) || empty($args['table'])) {
        	throw new \InvalidArgumentException('You execute sql table is empty.');
        }
        $table = $this->sql_table($args['table']);
        switch ($args['mode']) {
            case 'select':
                $sql = 'SELECT '.$this->sql_fields(isset($args['field'])? $args['field']:'*').' FROM '.$table;
                if (isset($args['where'])) {
                    $sql .= ' WHERE '.$this->sql_where($args['where']);
                }
                if (isset($args['group'])) {
                    $sql .= ' GROUP BY ' . $this->sql_group($args['group']);
                }
                if (isset($args['order'])) {
                    $sql .= ' ORDER BY ' . $this->sql_order($args['order']);
                }
                break;
            case 'update':
                $sql = 'UPDATE '.$table.' SET '.$this->sql_value($args['value'], 'update');
                if (isset($args['where'])) {
                    $sql .= ' WHERE '.$this->sql_where($args['where']);
                }
                break;
            case 'insert':
        		$sql  = ($args['is_replace'])? 'REPLACE ':'INSERT ';
                $sql .= 'INTO '.$table.' '.$this->sql_value($args['value'], 'insert');
                if (isset($args['duplicate'])) {
                	$sql .= ' ON DUPLICATE KEY UPDATE '.$this->sql_value($args['duplicate'], 'update');
                }
                break;
            case 'delete':
                $sql = 'DELETE FROM '.$table;
                if (!empty($args['where'])) {
                    $sql .= ' WHERE '.$this->sql_where($args['where']);
                }
                break;
        }
        if ($isprint) {
            $sql = $this->sql_2print($sql);
        }
        return $sql;
    }
    
    /**
     * 将带有标记的SQL语句转换成真实SQL语句
     * @param unknown $sql
     * @return mixed
     */
    private function sql_2print($sql) {
        foreach ($this->marks as $mark) {
            $mark = str_replace('?', '¤', $mark); 
            $pos  = strpos($sql, '?');
            if ($pos !== false) {
                if ($this->db === null) {
                    $this->connect();
                }               
                $s   = is_string($mark)? $this->db->quote($mark):$mark;
                $sql = substr($sql, 0, $pos) . $s . substr($sql, $pos + 1);
            }
        }
        if(strpos($sql, '¤')!== false) {  
            $sql = str_replace('¤', '?', $sql);//还原
        }
        return $sql;
    }
    
    /**
     * 字段值编码
     * @param unknown $value
     * @param unknown $type
     */
    private function field_value($value, $type) {
    	$result = '';
		switch ($type) {
			case orm::DT_SQL: 
				$result = $value; 
				break;
			case orm::DT_BINARY: 
				$result = '"'.$value.'"';
				break;
			default: 
				$result = $value;
				break;
		}
		return $result;
    }
    
    /**
     * 解析生成SQL语句中的字段列表(select)
     * @param array|string $fields
     * @return unknown|string
     */
    private function sql_fields($fields) {
    	if (!is_array($fields)) {
    		return $fields;
    	}
    	$sql = array();
    	foreach ($fields as $field) {
    		$sql[] = $field['alias']? $field['name'].' '.orm::OP_AS.' '.$field['alias']:$field['name'];
    	}
    	$sql = empty($sql)? '*':implode(',', $sql);
    	return $sql;
    }
    
    /**
     * 解析生成SQL语句中的字段列表(update&insert)
     * @param unknown $values
     * @param unknown $type
     * @return string
     */
    private function sql_value($values, $type) {
    	if (!is_array($values)) {
    		return '';
    	}
        if ($type == 'update') {//更新记录的Value部分
            $sql  = array();
            foreach ($values as $field=>$value) {
            	$field = '`'.$field.'`';
                if ($value['type'] == orm::DT_SQL) {
                    $sql[] = $field.'='.$value['value'];
                } else {
                    $sql[] = $field.'= ?';
                    $this->marks[] = $value['value'];
                }
            }
            $sql = implode(',', $sql);
        } else { //插入记录的Value部分
            $svalue = $sfield = array();
            foreach ($values as $field=>$value) {
            	$sfield[] = '`'.$field.'`';
                if ($value['type'] == orm::DT_SQL) {
                    $svalue[] = $value['value'];
                } else {
                    $svalue[] = '?';
                    $this->marks[] = $value['value'];
                }
            }
            $sql = '('.implode(',', $sfield).')VALUES('.implode(',', $svalue).')';
        }
        return $sql;
    }
    
    /**
     * 解析生成SQL语句中的表列表
     * @param array $tables
     * @return string|unknown
     */
    private function sql_table($tables) {
        $table = array_shift($tables);
        $sql   = ($table['alias'])? $table['name'].' '.orm::OP_AS.' '.$table['alias']:$table['name'];
        foreach ($tables as $table) {
        	$name  = $table['alias']? $table['name'].' '.orm::OP_AS.' '.$table['alias']:$table['name'];
        	$sql  .= $table['on']? ' LEFT JOIN '.$name.' ON ('.$table['on'].')':','.$name;
        }
        return $sql;
    }
    
    /**
     * 解析生成SQL语句中的条件列表
     * @param array $wheres
     * @return string|unknown
     */
    private function sql_where($wheres) {
        $sql     = array();
        $logical = false;
        foreach ($wheres as $where) {
            if ($logical && $where['name'] != ')' || $where['logical'] == orm::OP_NOT) {
                $sql[]   = $where['logical']; 
            }
            if ($where['name'] == '(' || $where['name'] == ')') {
                $sql[]   = $where['name'];
                $logical = ($where['name'] != '(');
                continue;
            }
       		switch ($where['opt']) {
       			case orm::OP_SQL:
       				$sql .= $where['name'];
       				break;
				case orm::OP_BETWEEN:
					if (is_array($where['value']) && count($where['value']) == 2) {
						$sql[] = $where['name'].' '.orm::OP_BETWEEN.' ? AND ?';
                        $this->marks = array_merge($this->marks, $where['value']);
					}
					break;
	 			case orm::OP_IN:
				case orm::OP_NOTIN:
             		if (is_array($where['value'])) {
                  		$fill  = implode(',', array_fill(0, count($where['value']), '?'));
              			$sql[] = $where['name'].' '.$where['opt'].' ('.$fill.')';
               			$this->marks = array_merge($this->marks, $where['value']);
               		} else {
                  		$sql[] = $where['name'].' '.$where['opt'].' ('.$where['value'].')';
               		}
             		break;
                case orm::OP_ISNULL:
 				case orm::OP_ISNOTNULL:
              		$sql[] = $where['name'].' '.$where['opt'];
                	break;
           		default :
                	if ($where['type'] == orm::DT_SQL) {
                		$sql[] = $where['name'].' '.$where['opt'].' '.$where['value'];
                  	} else {
              			$sql[] = $where['name'].' '.$where['opt'].' ?';
              			$this->marks[] = $where['value'];
					}
					break;
			}
			$logical = true;
        }
        return implode(' ', $sql);
    }
    
    /**
     * 解析生成SQL语句中的分组列表
     * @param unknown $groups
     * @return string
     */
    private function sql_group($groups) {
        $sql = implode(',', $groups);
        return $sql;
    }
    
    /**
     * 解析生成SQL语句中的排序列表
     * @param array $orders
     * @return string
     */
    private function sql_order($orders) {
        $sql = array();
        foreach ($orders as $order) {
            $sql[] = $order['name'] . ' ' . $order['order'];
        }
        $sql = implode(',', $sql);
        return $sql;
    }
    
    /**
     * 从结果集中提取记录
     * @param unknown $fetch_mode
     * @param string $isfirst
     */
    private function fetch($fetch_mode=orm::FETCH_ASSOC, $isfirst = false) {
    	$data = array();
        switch ($fetch_mode) {
            case orm::FETCH_ASSOC:  $fetch_mode = PDO::FETCH_ASSOC;  break;
            case orm::FETCH_NUM:    $fetch_mode = PDO::FETCH_NUM;    break;
            case orm::FETCH_BOTH:   $fetch_mode = PDO::FETCH_BOTH;   break;
            case orm::FETCH_OBJ:    $fetch_mode = PDO::FETCH_OBJ;    break;
        }
        if ($isfirst) {
            $data = $this->statement->fetch($fetch_mode);
        } else {
            $data = $this->statement->fetchAll($fetch_mode);
        }
        $this->statement->closeCursor();
        $this->statement = NULL;
        return $data;
    }
    
    /**
     * 获取一条记录
     * {@inheritDoc}
     * @see \classlib\database\parser::get_first()
     */
    public function get_first($sql, $fetch_mode=orm::FETCH_ASSOC) {
        $this->execute($sql.' LIMIT 1');
        return $this->fetch($fetch_mode, true);
    }
    
    /**
     * 返回所有记录
     * {@inheritDoc}
     * @see \classlib\database\parser::get_all()
     */
    public function get_all($sql, $offset=0, $count=-1, $fetch_mode=orm::FETCH_ASSOC) {
        if ($count > 0) {
            $sql .= ' LIMIT ' . $offset . ',' . $count;
        }
        $this->execute($sql);
        return $this->fetch($fetch_mode, false);
    }
    
    /**
     * 执行SQL并返回是否执行成功
     * {@inheritDoc}
     * @see \classlib\database\parser::execute()
     */
    public function execute($sql) {
        if (!$this->db) {
            $this->connect();
        }
        $this->statement = $this->db->prepare($sql);
        if (!$this->statement) {
        	$error  = $this->db->errorInfo();
        	throw new \PDOException($error[2], $error[1]);
        }
        if (strpos($sql, '?') === false) {
            $result = $this->statement->execute();
        } else {
            $result = $this->statement->execute($this->marks);
            $this->marks = array();
        }
        if ($result === false) {
        	$error  = $this->statement->errorInfo();
        	throw new \PDOException($error[2], $error[1]);
		}
        return $result;
    }
    
	/**
	 * 获取insert或者update影响的行
	 * {@inheritDoc}
	 * @see \classlib\database\parser::get_affected_rows()
	 */
    public function get_affected_rows() {
    	if (!$this->statement) {
    		return 0;
    	}
    	return $this->statement->rowCount();
    }
    
    /**
     * 返回最后一条插入行的序列值
     * {@inheritDoc}
     * @see \classlib\database\parser::last_insert_id()
     */
    public function last_insert_id() {
    	if (!$this->db) {
    		return 0;
    	}
        return (int)$this->db->lastInsertId();
    }

    /**
     * 初始化一个事务
     * {@inheritDoc}
     * @see \classlib\database\parser::begin_transaction()
     */
    public function begin_transaction() {
        if ($this->db === null) {
            $this->connect();
        }
        return $this->db->beginTransaction();
    }
    
    /**
     * 提交一个事务
     * {@inheritDoc}
     * @see \classlib\database\parser::commit()
     */
    public function commit() {
        return $this->db->commit();
    }
    
    /**
     * 回滚一个事务
     * {@inheritDoc}
     * @see \classlib\database\parser::rollback()
     */
    public function rollback() {
        return $this->db->rollBack();
    }
    
	/**
	 * 关闭数据连接
	 * {@inheritDoc}
	 * @see \classlib\database\parser::close()
	 */
    public function close() {
	    if (!$this->options[PDO::ATTR_PERSISTENT] && $this->db) {
	    	unset($this->db);
	    	$this->db = null;
    	}
    }
}